#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <list>
#include <functional>
#include <mutex>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
#include "cirQueue.hpp"
#include "Thread.hpp"

const int BUF_SIZE = 1024;
using serv_func_t = std::function<std::string(const std::string &str)>;

// 接收客户端的信息，并传回客户端

class UdpServer
{
public:
    UdpServer(uint16_t port, serv_func_t service)
        : _port(port), _service(service)
    {
        _p = Thread(1, RecvThreadRoutine, this);
        _c = Thread(2, BoardcastThreadRoutine, this);
    }

    void Initial()
    {
        // 1. 创建socket套接字（协议族，声明是哪种通信、哪种协议）
        int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd < 0)
        {
            // 创建套接字失败
            std::cerr << strerror(errno) << std::endl;
            exit(SOCKET_ERR);
        }
        _sockfd = sockfd;

        std::cout << "socket creat success: " << _sockfd << std::endl;

        // 2. 绑定特定的IP地址和端口号（地址族，标定网络通信地址）
        struct sockaddr_in local;
        bzero(&local, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        if (bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            // 绑定失败
            std::cerr << strerror(errno) << std::endl;
            exit(BIND_ERR);
        }

        std::cout << "socket bind success: "
                  << "port->" << _port << std::endl;
    }

    void Start()
    {
        _p.run();
        _c.run();

        _p.join();
        _c.join();
    }

    void Recv()
    {
        // 1.1 创建接收数据的缓冲区
        char buf[BUF_SIZE];
        memset(buf, 0, sizeof(buf));

        // 1.2 创建套接字
        struct sockaddr_in client;
        bzero(&client, sizeof(client));
        socklen_t len = sizeof(client); //!!!!!!!!!!!!

        // 1.3 从sock中接收客户端数据
        ssize_t n = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&client, &len);
        if (n < 0)
        {
            std::cerr << "recv error: " << strerror(errno) << std::endl;
            exit(RECV_ERR);
        }
        buf[n] = '\0';

        // 1.4 打包用户信息，并添加新用户
        std::string client_info = inet_ntoa(client.sin_addr);
        client_info += '-';
        client_info += std::to_string(ntohs(client.sin_port));
        AddOnlineUser(client_info, client);

        // 1.5 将消息投放到公共聊天窗口（即环形缓冲区）中
        std::string message = "[" + client_info + "] " + buf;
        _messages.push(message);
    }

    void Boardcast()
    {
        // 1. 取出环形缓冲区的头部数据，这是我们这次要广播的消息
        std::string message;
        _messages.pop(&message);

        // 2. 发给每一个在线用户
        // std::unique_lock<std::mutex> lck(_mtx);
        // //每次加锁再网络IO, 开销大
        // for (auto &usr : _online_users)
        // {
        //     std::cout << "send to " << usr.first << ": " << message << std::endl;

        //     ssize_t n = sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&usr.second, sizeof(usr.second));
        //     if (n < 0)
        //     {
        //         std::cerr << "send error: " << strerror(errno) << std::endl;
        //         exit(SEND_ERR);
        //     }
        // }

        // 先加锁拷贝一份在线用户信息副本（公有->私有）
        list<struct sockaddr_in> sins;
        {
            std::unique_lock<std::mutex> lck(_mtx);
            for (auto &usr : _online_users)
            {
                std::cout << "send to " << usr.first << ": " << message << std::endl;
                sins.push_back(usr.second);
            }
        }
        // 再用线程私有的副本进行网络IO将信息传给客户端
        for (auto &sin : sins)
        {
            ssize_t n = sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&sin, sizeof(sin));
            if (n < 0)
            {
                std::cerr << "send error: " << strerror(errno) << std::endl;
                exit(SEND_ERR);
            }
        }
    }

    static void *RecvThreadRoutine(void *args)
    {
        UdpServer *ts = static_cast<UdpServer *>(args);
        while (true)
        {
            ts->Recv();
        }
        return nullptr;
    }

    static void *BoardcastThreadRoutine(void *args)
    {
        UdpServer *ts = static_cast<UdpServer *>(args);
        while (true)
        {
            ts->Boardcast();
        }
        return nullptr;
    }

private:
    void AddOnlineUser(const std::string &user, struct sockaddr_in &sin)
    {
        std::unique_lock<std::mutex> lck(_mtx);
        // if user is a new client, add it into online_users, else do nothing
        size_t size = _online_users.size();
        _online_users[user] = sin;
        if (_online_users.size() > size)
            std::cout << "新用户加入: " << user << std::endl;
    }

private:
    int _sockfd;                                                       // 套接字文件fd
    uint16_t _port;                                                    // 服务器端口号
    serv_func_t _service;                                              // 业务处理函数
    cirQueue<std::string> _messages;                                   // 存储用户消息的环形队列
    std::unordered_map<std::string, struct sockaddr_in> _online_users; // 在线用户信息表（用户ip和port）
    Thread _p;                                                         // recv线程（生产者）
    Thread _c;                                                         // boardcast线程（消费者）
    std::mutex _mtx;                                                   // 保护_online_users的锁
};
